package com.all.learning.guava.other;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import com.google.common.base.Function;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.CacheStats;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalCause;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.MapMaker;

/**
 * 　expireAfterAccess(long, TimeUnit) 这个方法是根据某个键值对最后一次访问之后多少时间后移除 　<br>
 * 　expireAfterWrite(long, TimeUnit) 这个方法是根据某个键值对被创建或值被替换后多少时间移除
 * 
 * @author luxw
 * @version 1.0,2014-6-25 下午2:39:31
 */
public class GGcache {

	@Test
	public void TestLoadingCache() throws Exception {
		StringRemovalListener removalListener = new StringRemovalListener();

		LoadingCache<String, String> cache = CacheBuilder.newBuilder().expireAfterWrite(500, TimeUnit.MILLISECONDS)//
				.maximumSize(1000)//
				.removalListener(removalListener)//
				.recordStats()//
				.build(getCacheLoader());
		
		
		String queryKey = "firstName:bob";
		String personList = cache.get(queryKey);
		String personListII = cache.get(queryKey);
		Thread.sleep(500);
		String personListIII = cache.get(queryKey);
		assertThat(personList == personListII, is(true));
		assertThat(personList == personListIII, is(false));

		RemovalNotification<String, String> removalNotification = removalListener.getRemovalNotification();
		assertThat(removalNotification.getValue() == personList, is(true));
		assertThat(removalNotification.getCause(), is(RemovalCause.EXPIRED));
		// assertThat(removalListener.getRemovalNotification().getCause(),is(RemovalCause.SIZE));

		CacheStats stats = cache.stats();
		assertThat(stats.hitCount(), is(1l));
		assertThat(stats.loadCount(), is(2l));
		assertThat(stats.missCount(), is(2l));
		assertThat(stats.requestCount(), is(100l));
	}

	private class StringRemovalListener implements RemovalListener<String, String> {
		private RemovalNotification<String, String> removalNotification;

		@Override
		public void onRemoval(RemovalNotification<String, String> removalNotification) {
			this.removalNotification = removalNotification;

		}

		public RemovalNotification<String, String> getRemovalNotification() {
			return removalNotification;
		}
	}

	private CacheLoader<String, String> getCacheLoader() {
		return new CacheLoader<String, String>() {
			@Override
			public String load(String key) throws Exception {
				String strProValue = "hello " + key + "!";
				return strProValue;
			}
		};
	}

	@Test
	public void testCacheLoadedAfterFirstRequestThenCached() throws Exception {
		LoadingCache<String, String> cache = CacheBuilder.newBuilder().recordStats().build(CacheLoader.from(getFunction()));
		String queryKey = "lastName:smith";
		String personList = null;
		for (int i = 0; i < 100; i++) {
			personList = cache.get(queryKey);
		}
		CacheStats stats = cache.stats();
		assertThat(stats.hitCount(), is(99l));
		assertThat(stats.loadCount(), is(1l));
		assertThat(stats.missCount(), is(1l));
		assertThat(stats.requestCount(), is(100l));

	}

	private Function<String, String> getFunction() {
		return new Function<String, String>() {
			@Override
			public String apply(String searchKey) {
				try {
					return "1234" + searchKey;
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		};
	}

	@Test
	public void testCache() {
		String key = "key1";
		Cache<String, Object> simpleCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();
		Object value = null;
		try {
			value = simpleCache.get(key, new Callable<Object>() {
				@Override
				public Object call() throws Exception {
					System.out.println("获取缓存数据数据中。。。");
					return "返回缓存中的值";
				}
			});
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		// simpleCache.put(key, value);
		// simpleCache.invalidateAll();
		// simpleCache.invalidate(key);
	}

	/**
	 * 
	 * 　Guava Cache有两种创建方式： 　　 1. cacheLoader 　　 2. callable callback
	 * 
	 * 　通过这两种方法创建的cache，和通常用map来缓存的做法比，不同在于，这两种方法都实现了一种逻辑——从缓存中取key X的值，<br>
	 * 如果该值已经缓存过了，则返回缓存中的值，如果没有缓存过，可以通过某个方法来获取这个值。<br>
	 * 但不同的在于cacheloader的定义比较宽泛，是针对整个cache定义的，<br>
	 * 可以认为是统一的根据key值load value的方法。而callable的方式较为灵活，允许你在get的时候指定。<br>
	 * 
	 * 
	 * 
	 * 
	 */

	public void testMap() {
		ConcurrentMap<String, String> books = new MapMaker().concurrencyLevel(2).makeMap();
	}
	
	


	
//	//配置CacheBuilder的字符串
//	String spec = "concurrencyLevel=10,expireAfterAccess=5m,softValues";
//	//解析字符串，创建CacheBuilderSpec实例
//	CacheBuilderSpec cacheBuilderSpec = CacheBuilderSpec.parse(spec);
//	//通过CacheBuilderSpec实例构造CacheBuilder实例
//	CacheBuilder cacheBuilder = CacheBuilder.from(cacheBuilderSpec);
//	//ticker：设置缓存条目过期时间
//	//removalListener：监听缓存条目的移除
//	cacheBuilder.ticker(Ticker.systemTicker())
//	        .removalListener(new TradeAccountRemovalListener())
//	        .build(new CacheLoader<String, TradeAccount>() {
//	            @Override
//	            public TradeAccount load(String key) throws
//	                    Exception {
//	                return
//	                        tradeAccountService.getTradeAccountById(key);
//	            }
//	        });
	
	//http://blog.csdn.net/desilting/article/details/11768773
//	http://my.oschina.net/realfighter/blog/358871
}
